package com.ceair.authorization.handler;

import com.ceair.entity.model.Result;
import com.ceair.util.JsonUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.util.UrlUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * @author wangbaohai
 * @ClassName LoginFailureHandler
 * @description: TODO
 * @date 2024年11月20日
 * @version: 1.0.0
 */
@Slf4j
public class LoginFailureHandler implements AuthenticationFailureHandler {

    private final String loginPageUri;

    private final AuthenticationFailureHandler authenticationFailureHandler;

    /**
     * 构造函数，用于初始化登录失败处理器
     *
     * @param loginPageUri 登录页面的URI，用于构建登录失败时的重定向URL
     */
    public LoginFailureHandler(String loginPageUri) {
        // 存储登录页面的URI
        this.loginPageUri = loginPageUri;

        // 构建登录失败时的重定向URL
        String loginFailureUrl = this.loginPageUri + "?error";

        // 初始化认证失败处理器，重定向到登录失败的URL
        this.authenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler(loginFailureUrl);
    }

    /**
     * 当用户登录认证失败时调用的方法
     *
     * @param request HttpServletRequest对象，包含本次请求的相关信息
     * @param response HttpServletResponse对象，用于向客户端发送响应
     * @param exception 认证失败的异常信息，说明登录失败的原因
     * @throws IOException 当向客户端发送响应时发生输入输出异常
     */
    @Override
    @SneakyThrows
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException {
        // 记录登录失败的日志，包括失败原因
        log.debug("登录失败，原因：{}", exception.getMessage());

        // 如果是绝对路径(前后端分离)
        if (UrlUtils.isAbsoluteUrl(this.loginPageUri)) {
            // 认证失败，写回401与具体的异常
            Result<String> success = Result.error(HttpStatus.UNAUTHORIZED.value(), exception.getMessage());
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.getWriter().write(JsonUtils.objectCovertToJson(success));
            response.getWriter().flush();
        } else {
            // 登录页面为认证服务的相对路径，交由默认的认证失败处理器处理
            authenticationFailureHandler.onAuthenticationFailure(request, response, exception);
        }
    }

}
